package com.lancoo.ccas52.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.setting.SettingUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lancoo.ccas52.entity.*;
import com.lancoo.ccas52.exception.BizException;
import com.lancoo.ccas52.mapper.*;
import com.lancoo.ccas52.pojo.constant.GlobalConstant;
import com.lancoo.ccas52.pojo.response.Scheme;
import com.lancoo.ccas52.pojo.response.score.Retake;
import com.lancoo.ccas52.pojo.vo.*;
import com.lancoo.ccas52.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lancoo.ccas52.util.BatchInsert;
import com.lancoo.ccas52.util.ExcelFormatUtil;
import com.lancoo.ccas52.util.WeekUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * <p>
 * 教务获取教学计划,相当于专业版本 服务实现类
 * </p>
 *
 * @author quin.song
 * @since 2022-12-30
 */
@Service
@Slf4j
public class PlanTeachingServiceImpl extends ServiceImpl<PlanTeachingMapper, PlanTeaching> implements PlanTeachingService {

    @Resource
    private TaskInfoService taskInfoService;
    @Resource
    private CourseService courseService;
    @Resource
    private SegmentService segmentService;
    @Resource
    private PlanTeachingStudentService planTeachingStudentService;
    @Resource
    private TeachingClassService teachingClassService;
    @Resource
    private PlanTeachingCourseSegmentService planTeachingCourseSegmentService;
    @Resource
    private StudentRetakeService studentRetakeService;
    @Resource
    private CourseSettingService courseSettingService;
    @Resource
    private SegmentSettingService segmentSettingService;

    @Resource
    private PlanTeachingStudentMapper planTeachingStudentMapper;
    @Resource
    private PlanTeachingMapper planTeachingMapper;
    @Resource
    private TeachingClassStudentMapper teachingClassStudentMapper;
    @Resource
    private PlanTeachingCourseSegmentMapper planTeachingCourseSegmentMapper;
    @Resource
    private CourseMapper courseMapper;
    @Resource
    private SegmentMapper segmentMapper;

    @Override
    public void syncEducational(Integer taskId, List<Scheme> schemeList, String term, List<String> gradeList) {
        //事务会导致缓存空列表
        List<PlanTeaching> planTeachings = new ArrayList<>();
//        Set<Major> majors = new HashSet<>();
        List<PlanTeachingCourseSegment> planTeachingCourseSegments = new ArrayList<>();
        Set<Course> courses = new HashSet<>();
        Set<Segment> segments = new HashSet<>();
        List<PlanTeachingStudent> planTeachingStudents = new ArrayList<>();
        for (Scheme scheme : schemeList) {
            if (!gradeList.contains(scheme.getGradeID())) {
                continue;
            }
            //如果该教学计划存在课程，则置为true， 同步该条教学计划
            boolean isExist = false;
            //根据学期判断是否需要上该课
            for (Scheme.CourseListDTO plan : scheme.getCourseList()) {
                //不同步辅修计划  因为双学位包括辅修计划
                if (scheme.getSchemeType() == 2) {
                    continue;
                }
                int openingTerm = WeekUtil.getOpeningTerm(term, scheme.getGradeName());
                //判断开课学期是否包含该课程
                if (!plan.getTerms().contains(String.valueOf(openingTerm))) {
                    continue;
                }
                isExist = true;
                int flag = 0;
                //todo 过滤该学期课程环节
                if ("1".equals(plan.getPlanType())) {
                    Course course = new Course()
                            .setTaskId(taskId)
                            .setBaseCourseId(plan.getPlanID())
                            .setCourseName(plan.getCourseName())
                            .setCourseTypeId(plan.getPlanKindID())
                            .setCourseType(plan.getPlanKindName())
                            .setCourseNature(plan.getCourseProp())
                            .setMajorProp(plan.getMajorProp())
                            .setCourseCode(plan.getCourseCode())
                            .setCollegeId(plan.getCollegeID())
                            .setCollegeName(plan.getCollegeName())
                            .setSubjectId(plan.getSubjectID())
                            .setSubjectName(plan.getSubjectName())
                            .setCredit(plan.getCredit())
                            .setLectureHour(plan.getLectureHours())
                            .setExperimentalHour(plan.getExperimentHours())
                            .setComputerHour(plan.getOperationHours())
                            .setOtherHour(plan.getOtherHours())
                            .setStudyNature(scheme.getSchemeType().equals(1) ? 1 : 2)
                            .setIsEnable(0);
                    //修读性质 1必修 2选修 3必修选修
                    if (courses.contains(course)) {
                        Course existCourse = courses.stream().filter(e -> e.equals(course)).findFirst().get();
                        if (!existCourse.getStudyNature().equals(course.getStudyNature())) {
                            course.setStudyNature(3);
                            courses.remove(existCourse);
                            courses.add(course);
                        }
                    } else {
                        courses.add(course);
                    }
                    flag = 1;
                } else if ("2".equals(plan.getPlanType())) {
                    Segment segment = new Segment()
                            .setTaskId(taskId)
                            .setBaseSegmentId(plan.getPlanID())
                            .setSegmentName(plan.getCourseName())
                            .setSegmentCode(plan.getCourseCode())
                            .setSegmentTypeId(plan.getPlanKindID())
                            .setSegmentType(plan.getPlanKindName())
                            .setCollegeId(plan.getCollegeID())
                            .setCollegeName(plan.getCollegeName())
                            .setCredit(plan.getCredit())
                            .setWeekNum(plan.getTotalHour())
                            .setStudyNature(scheme.getSchemeType().equals(1) ? 1 : 2)
                            .setIsEnable(0);
                    if (segments.contains(segment)) {
                        Segment existSegment = segments.stream().filter(e -> e.equals(segment)).findFirst().get();
                        if (!existSegment.getStudyNature().equals(segment.getStudyNature())) {
                            segment.setStudyNature(3);
                            segments.remove(existSegment);
                            segments.add(segment);
                        }
                    } else {
                        segments.add(segment);
                    }
                    flag = 2;
                }
                //添加课程环节中间表
                planTeachingCourseSegments.add(new PlanTeachingCourseSegment()
                        .setTaskId(taskId)
                        .setBaseTeachingPlanId(scheme.getSchemeID())
                        .setBaseCourseId(plan.getPlanID())
                        .setFlag(flag));
            }
            if (isExist) {
                //教学计划
                planTeachings.add(scheme.toPlanTeaching(taskId));
                //专业
//                majors.add(scheme.toMajor(taskId));
                //教学计划学生
                planTeachingStudents.addAll(scheme.toPlanTeachingStudent(taskId));
            }
        }
        //插入、更新或删除教学计划
        this.syncPlanTeaching(taskId, planTeachings);
        this.syncPlanSegCou(taskId, planTeachingCourseSegments);
        this.syncPlanStudent(taskId, planTeachingStudents);
//        majorService.syncMajor(taskId, majors);
        courseService.syncCourse(taskId, courses);
        courseService.syncSegment(taskId, segments);

    }

    public void syncPlanTeaching(Integer taskId, List<PlanTeaching> syncPlanTeaching) {
        List<PlanTeaching> insertList = new ArrayList<>();
        //获取数据库教学计划
        List<PlanTeaching> planTeachingList = this.list(new LambdaQueryWrapper<PlanTeaching>().eq(PlanTeaching::getTaskId, taskId));
        for (PlanTeaching planTeaching : syncPlanTeaching) {
            if (!planTeachingList.contains(planTeaching)) {
                insertList.add(planTeaching);
            }
        }
        planTeachingList.removeAll(syncPlanTeaching);
        List<Long> deleteIds = planTeachingList.stream().map(PlanTeaching::getTeachingPlanId).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(deleteIds)) {
            this.removeBatchByIds(deleteIds);
        }
        if (CollUtil.isNotEmpty(insertList)) {
            planTeachingMapper.addBatch(insertList);
        }
    }

    public void syncPlanSegCou(Integer taskId, List<PlanTeachingCourseSegment> syncPlanCS) {
        List<PlanTeachingCourseSegment> insertList = new ArrayList<>();
        //获取数据库中间表
        List<PlanTeachingCourseSegment> planCSList = planTeachingCourseSegmentService.list(new LambdaQueryWrapper<PlanTeachingCourseSegment>().eq(PlanTeachingCourseSegment::getTaskId, taskId));
        for (PlanTeachingCourseSegment planTeachingCourseSegment : syncPlanCS) {
            if (!planCSList.contains(planTeachingCourseSegment)) {
                insertList.add(planTeachingCourseSegment);
            }
        }
        planCSList.removeAll(syncPlanCS);
        List<Long> deleteIds = planCSList.stream().map(PlanTeachingCourseSegment::getId).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(deleteIds)) {
            planTeachingCourseSegmentService.removeBatchByIds(deleteIds);
        }
        if (CollUtil.isNotEmpty(insertList)) {
            planTeachingCourseSegmentMapper.addBatch(insertList);
        }
    }

    public void syncPlanStudent(Integer taskId, List<PlanTeachingStudent> syncPlanTeachingStudent) {
        List<PlanTeachingStudent> insertList = new ArrayList<>();
        //获取数据库中间表
        List<PlanTeachingStudent> studentList = planTeachingStudentService.list(new LambdaQueryWrapper<PlanTeachingStudent>().eq(PlanTeachingStudent::getTaskId, taskId));
        for (PlanTeachingStudent planTeachingStudent : syncPlanTeachingStudent) {
            if (!studentList.contains(planTeachingStudent)) {
                insertList.add(planTeachingStudent);
            }
        }
        studentList.removeAll(syncPlanTeachingStudent);
        List<Long> deleteIds = studentList.stream().map(PlanTeachingStudent::getId).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(deleteIds)) {
            planTeachingStudentService.removeBatchByIds(deleteIds);
        }
        if (CollUtil.isNotEmpty(insertList)) {
            //批量插入教学班学生，大数据批量插入
            Consumer<List<PlanTeachingStudent>> studentConsumer = o -> planTeachingStudentMapper.addBatch(o);
            BatchInsert.batchSplitInsert(insertList, studentConsumer);
        }
    }


    @Override
    public TeachingPlanCount getPlanTeaching(Integer taskId, String collegeId, Integer type, String gradeId) {
        TeachingPlanCount teachingPlanCount = new TeachingPlanCount();
        List<TeachingPlanDto> teachingPlanDtos = planTeachingMapper.getTeachingPlanByCondition(taskId, collegeId, type, gradeId);
        long total = teachingPlanDtos.size();
        teachingPlanCount.setTotal(total);
        long majorTotal = teachingPlanDtos.stream().filter(a -> a.getType().equals(1)).count();
        teachingPlanCount.setMajorTotal(majorTotal);
        teachingPlanCount.setMinorTotal(total - majorTotal);
        teachingPlanCount.setTeachPlans(teachingPlanDtos);

        return teachingPlanCount;
    }

    @Override
    public List<TeachingPlanDto> getPlanTeachingByIds(List<Long> planTeachingIds) {
        List<TeachingPlanDto> teachingPlanById = new ArrayList<>();
        if (CollUtil.isNotEmpty(planTeachingIds)) {
            teachingPlanById = planTeachingMapper.getTeachingPlanById(planTeachingIds);
        }
        return teachingPlanById;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePlanTeaching(Integer taskId, List<Long> teachPlanIds, Integer isEnable) {

        //确定之前先修改教学计划 课程 环节状态
        this.lambdaUpdate().set(PlanTeaching::getIsEnable, 0).eq(PlanTeaching::getTaskId, taskId).update();
        courseService.lambdaUpdate().set(Course::getIsEnable, 0).eq(Course::getTaskId, taskId).update();
        segmentService.lambdaUpdate().set(Segment::getIsEnable, 0).eq(Segment::getTaskId, taskId).update();

        List<PlanTeachingCourseSegment> planTeachingCourseSegmentByTeachingPlanIds = planTeachingMapper.getPlanTeachingCourseSegmentByTeachingPlanIds(taskId, teachPlanIds);
        //课程id集合
        List<String> courseList = planTeachingCourseSegmentByTeachingPlanIds.stream().filter(a -> a.getFlag().equals(1)).map(b -> b.getBaseCourseId()).collect(Collectors.toList());
        //环节id集合
        List<String> segmentList = planTeachingCourseSegmentByTeachingPlanIds.stream().filter(a -> a.getFlag().equals(2)).map(b -> b.getBaseCourseId()).collect(Collectors.toList());

        if (CollUtil.isNotEmpty(courseList)) {
            courseService.update(new LambdaUpdateWrapper<Course>()
                    .eq(Course::getTaskId, taskId)
                    .in(Course::getBaseCourseId, courseList)
                    .set(Course::getIsEnable, isEnable));
        }
        if (CollUtil.isNotEmpty(segmentList)) {
            segmentService.update(new LambdaUpdateWrapper<Segment>()
                    .eq(Segment::getTaskId, taskId)
                    .in(Segment::getBaseSegmentId, segmentList)
                    .set(Segment::getIsEnable, isEnable));
        }
        //更新教学计划表
        this.update(new LambdaUpdateWrapper<PlanTeaching>().in(PlanTeaching::getTeachingPlanId, teachPlanIds).set(PlanTeaching::getIsEnable, isEnable));

    }


    @Override
    public List<PlanTeachingCourseSegment> addPlanSegCou(List<AddPlanSegCon> addPlanSegCons) {
        List<PlanTeachingCourseSegment> planTeachingCourseSegments = BeanUtil.copyToList(addPlanSegCons, PlanTeachingCourseSegment.class);
        planTeachingCourseSegmentMapper.addBatch(planTeachingCourseSegments);
        //修改对应课程修读性质 课程只有一个，教学计划暂时只有一个
        AddPlanSegCon addPlanSegCon = addPlanSegCons.get(0);
        if (addPlanSegCon.getFlag().equals(1)) {
            courseService.update(new LambdaUpdateWrapper<Course>()
                    .eq(Course::getTaskId, addPlanSegCon.getTaskId())
                    .eq(Course::getBaseCourseId, addPlanSegCon.getBaseCourseId())
                    .set(Course::getStudyNature, addPlanSegCon.getStudyNature()));
        } else {
            segmentService.update(new LambdaUpdateWrapper<Segment>()
                    .eq(Segment::getTaskId, addPlanSegCon.getTaskId())
                    .eq(Segment::getBaseSegmentId, addPlanSegCon.getBaseCourseId())
                    .set(Segment::getStudyNature, addPlanSegCon.getStudyNature()));
        }
        return planTeachingCourseSegments;
    }

    @Override
    public List<TeachingPlanDto> getTeachingPlanNoCourse(Integer taskId, Integer flag, String baseCourseId, String collegeId) {
        return planTeachingMapper.getTeachingPlanNoCourse(taskId, flag, baseCourseId, collegeId);
    }

    @Override
    public List<CampusPlanTeachingVo> getTeachingPlanByCourse(Long courseId, Integer studyNature, Integer teachingClassType) {
        List<CampusPlanTeachingVo> campusPlanTeachingList = new ArrayList<>();
        Map<String, List<PlanTeaching>> planTeachingGroupCampus = planTeachingMapper.getPlanTeachingByCourseId(courseId, studyNature, null)
                .stream().collect(Collectors.groupingBy(PlanTeaching::getCampusId));
        planTeachingGroupCampus.forEach((k, v) -> {
            CampusPlanTeachingVo campusPlanTeachingVo = new CampusPlanTeachingVo();
            campusPlanTeachingVo.setCampusId(k);
            campusPlanTeachingVo.setCampusName(v.get(0).getCampusName());
            //培养计划修读学生
            Integer studentNumCourse = this.getStudentNumByCourseAndNature(1, courseId, studyNature, k).size();
            campusPlanTeachingVo.setStudentNumPlan(studentNumCourse);
            campusPlanTeachingVo.setStudentNum(studentNumCourse);
            if (teachingClassType == 3) {
                Long classNum = teachingClassService.lambdaQuery()
                        .eq(TeachingClass::getCourseId, courseId)
                        .eq(TeachingClass::getStudyNature, studyNature)
                        .eq(TeachingClass::getCampusId, k)
                        .eq(TeachingClass::getFlag, 1)
                        .ne(TeachingClass::getIsEnable, 0)
                        .eq(TeachingClass::getTeachingClassType, 3).count();
                campusPlanTeachingVo.setClassNum(classNum.intValue());
            } else {
                Long classNum = teachingClassService.lambdaQuery()
                        .eq(TeachingClass::getCourseId, courseId)
                        .eq(TeachingClass::getStudyNature, studyNature)
                        .eq(TeachingClass::getCampusId, k)
                        .eq(TeachingClass::getFlag, 1)
                        .ne(TeachingClass::getTeachingClassType, 3).count();
                campusPlanTeachingVo.setClassNum(classNum.intValue());
            }
            campusPlanTeachingVo.setPlanTeachingList(v);
            if (studentNumCourse > 0) {
                campusPlanTeachingList.add(campusPlanTeachingVo);
            }
        });
        return campusPlanTeachingList;
    }

    @Override
    public List<CampusPlanTeachingVo> getTeachingPlanBySegment(Long segmentId, Integer studyNature) {
        List<CampusPlanTeachingVo> campusPlanTeachingList = new ArrayList<>();
        Map<String, List<PlanTeaching>> planTeachingGroupCampus = planTeachingMapper.getPlanTeachingBySegmentId(segmentId, studyNature, null)
                .stream().collect(Collectors.groupingBy(PlanTeaching::getCampusId));
        planTeachingGroupCampus.forEach((k, v) -> {
            CampusPlanTeachingVo campusPlanTeachingVo = new CampusPlanTeachingVo();
            campusPlanTeachingVo.setCampusId(k);
            campusPlanTeachingVo.setCampusName(v.get(0).getCampusName());
            //培养计划修读学生
            Integer studentNumCourse = this.getStudentNumByCourseAndNature(2, segmentId, studyNature, k).size();
            //重修学生
//            int studentNumRetake = studentRetakeService.getStudentRetakeByCourse(segmentId, 2, studyNature, k).size();
            campusPlanTeachingVo.setStudentNumPlan(studentNumCourse);
//            campusPlanTeachingVo.setStudentNumRetake(studentNumRetake);
            campusPlanTeachingVo.setStudentNum(studentNumCourse);
            Long classNum = teachingClassService.lambdaQuery()
                    .eq(TeachingClass::getCourseId, segmentId)
                    .eq(TeachingClass::getStudyNature, studyNature)
                    .eq(TeachingClass::getCampusId, k)
                    .eq(TeachingClass::getFlag, 2)
                    .eq(TeachingClass::getTeachingClassType, 3).count();
            campusPlanTeachingVo.setClassNum(classNum.intValue());
            campusPlanTeachingVo.setPlanTeachingList(v);
            campusPlanTeachingList.add(campusPlanTeachingVo);
        });
        return campusPlanTeachingList;
    }

    @Override
    @Cacheable(cacheNames = "CourseStudent", key = "#flag+'-'+#courseId+'-'+#studyNature+'-'+#campusId")
    public List<Student> getStudentNumByCourseAndNature(Integer flag, Long courseId, Integer studyNature, String campusId) {
        List<PlanTeaching> planTeachings = new ArrayList<>();
        //根据课程id获取开课计划
        if (flag == 1) {
            planTeachings = planTeachingMapper.getPlanTeachingByCourseId(courseId, studyNature, campusId);
        } else {
            planTeachings = planTeachingMapper.getPlanTeachingBySegmentId(courseId, studyNature, campusId);
        }
        if (CollUtil.isEmpty(planTeachings)) {
            log.warn("课程id：{},修读性质：{} 的课程没有对应的教学计划", courseId, studyNature);
            return new ArrayList<>();
        }
        //根据教学计划统计学生数量
        Integer taskId = planTeachings.get(0).getTaskId();
        List<String> baseTeachingPlanIds = planTeachings.stream().map(PlanTeaching::getBaseTeachingPlanId).collect(Collectors.toList());
        List<Student> studentByPlanId = planTeachingMapper.getStudentByPlanId(taskId, baseTeachingPlanIds)
                .stream().distinct().collect(Collectors.toList());
        return studentByPlanId;
    }

    @Override
    @Deprecated
    @Cacheable(cacheNames = "PlanCourseStudent", key = "#teachingPlanId+'-'+#courseId+'-'+#flag")
    public List<Student> getStudentNumByTeachingPlanId(Long teachingPlanId, Long courseId, Integer flag) {
        PlanTeaching planTeaching = this.getById(teachingPlanId);
//        Course course = courseService.getById(courseId);
//        TaskInfo taskInfo = taskInfoService.getById(planTeaching.getTaskId());
        List<Student> studentByPlanId = planTeachingMapper.getStudentByPlanId(planTeaching.getTaskId(), Collections.singletonList(planTeaching.getBaseTeachingPlanId()));
        //除去免修学生
//        List<Retake> exemptStudent = yunBasicService.getExemptStudent(planTeaching.getTaskId(), taskInfo.getSchoolId(), taskInfo.getTerm());
//        exemptStudent.stream().filter(a -> a.getCourseNO().equals(course.getBaseCourseId())).forEach(b -> {
//            studentByPlanId.removeIf(c -> c.getBaseStudentId().equals(b.getStudentID()));
//        });
        return studentByPlanId;
    }

    @Override
    public List<StudentGroupClass> getStudentListByPlanId(Long teachingPlanId) {
        List<StudentGroupClass> studentGroupClassList = new ArrayList<>();
        PlanTeaching planTeaching = this.getById(teachingPlanId);
        List<String> baseTeachingIds = Arrays.asList(planTeaching.getBaseTeachingPlanId());
        List<Student> studentIdList = planTeachingMapper.getStudentByPlanId(planTeaching.getTaskId(), baseTeachingIds);
        //todo 根据学生名过滤?

        //根据学院id 名字分组
        Map<Pair<String, String>, List<Student>> listMap = studentIdList.stream().collect(Collectors.groupingBy(p -> Pair.of(p.getClassId(), p.getClassName())));
        listMap.entrySet().forEach(o -> {
            StudentGroupClass studentGroupClass = new StudentGroupClass();
            studentGroupClass.setClassId(o.getKey().getKey());
            studentGroupClass.setClassName(o.getKey().getValue());
            studentGroupClass.setStudentList(o.getValue());
            studentGroupClassList.add(studentGroupClass);
        });
        return studentGroupClassList;
    }

    @Override
    public List<StudentGroupClass> getStudentListByCourse(Integer flag, Long courseId, Integer studyNature, String campusId, boolean isDisplaySelected) {
        List<StudentGroupClass> studentGroupClassList = new ArrayList<>();
        List<Student> studentNumByCourseAndNature = this.getStudentNumByCourseAndNature(flag, courseId, studyNature, campusId);
        if (CollUtil.isEmpty(studentNumByCourseAndNature)) {
            return studentGroupClassList;
        }
        if (!isDisplaySelected) {
            //排除已经选择该课程的学生。先获取教学班再获取教学班学生
            List<Long> teachingClassIds = teachingClassService.lambdaQuery()
                    .eq(TeachingClass::getCourseId, courseId)
                    .eq(TeachingClass::getStudyNature, studyNature)
                    .eq(TeachingClass::getCampusId, campusId)
                    .eq(TeachingClass::getFlag, flag)
                    .ne(TeachingClass::getTeachingClassType, 3).list()
                    .stream().map(TeachingClass::getTeachingClassId).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(teachingClassIds)) {
                List<Long> studentIds = teachingClassStudentMapper.selectList(new QueryWrapper<TeachingClassStudent>().in("teaching_class_id", teachingClassIds))
                        .stream().map(TeachingClassStudent::getStudentId).collect(Collectors.toList());
                if (CollUtil.isNotEmpty(studentIds)) {
                    studentNumByCourseAndNature.removeIf(a -> studentIds.contains(a.getStudentId()));
                }
            }

        }

        //根据学院id 名字分组
        Map<Pair<String, String>, List<Student>> listMap = studentNumByCourseAndNature.stream().collect(Collectors.groupingBy(p -> Pair.of(p.getClassId(), p.getClassName())));
        listMap.entrySet().forEach(o -> {
            StudentGroupClass studentGroupClass = new StudentGroupClass();
            studentGroupClass.setClassId(o.getKey().getKey());
            studentGroupClass.setClassName(o.getKey().getValue());
            studentGroupClass.setStudentList(o.getValue());
            studentGroupClassList.add(studentGroupClass);
        });
        return studentGroupClassList;
    }


    @Override
    public List<Student> getMinorStudentByCourseId(Long courseId) {
        //获取包含该课程的辅修教学计划
        List<Student> students = new ArrayList<>();
        List<PlanTeaching> planTeachings = planTeachingMapper.getPlanTeachingByCourseId(courseId, 2, null);
        if (CollUtil.isNotEmpty(planTeachings)) {
            Integer taskId = planTeachings.get(0).getTaskId();
            List<String> baseTeachingIds = planTeachings.stream().map(PlanTeaching::getBaseTeachingPlanId).collect(Collectors.toList());
            students = planTeachingMapper.getStudentByPlanId(taskId, baseTeachingIds);
        }
        return students;
    }

    @Override
    public List<TeachingClass> generationTeachingClass(Integer taskId, Long courseId, Integer studyNature, Integer teachingClassNum, Integer teachingClassType, Integer classNum, Integer flag, String campusId, String campusName, Integer headPointer) {
        List<TeachingClassInput> list = new ArrayList<>();
        if (flag == 1) {
            Course course = courseService.getById(courseId);
            //教学班名称的生成：高数1班-234，其中234代表教学班的id
            for (Integer i = headPointer; i < teachingClassNum + headPointer; i++) {
                String teachingClassName = StrUtil.format("{}{}班", course.getCourseName(), (i + 1));
                TeachingClassInput teachingClassInput = TeachingClassInput.builder()
                        .taskId(taskId)
                        .teachingClassName(teachingClassName)
                        .courseId(courseId)
                        .collegeId(course.getCollegeId())
                        .classNum(classNum)
                        .teachingClassType(teachingClassType)
                        .flag(flag)
                        .studyNature(studyNature)
                        .campusId(campusId)
                        .campusName(campusName)
                        .isEnable(teachingClassType == 3 ? 1 : 2)
                        .build();
                list.add(teachingClassInput);
            }
        } else {
            Segment segment = segmentService.getById(courseId);
            for (Integer i = headPointer; i < teachingClassNum + headPointer; i++) {
                String teachingClassName = StrUtil.format("{}{}班", segment.getSegmentName(), (i + 1));
                TeachingClassInput teachingClassInput = TeachingClassInput.builder()
                        .taskId(taskId)
                        .teachingClassName(teachingClassName)
                        .courseId(courseId)
                        .collegeId(segment.getCollegeId())
                        .classNum(classNum)
                        .teachingClassType(teachingClassType)
                        .flag(flag)
                        .studyNature(studyNature)
                        .campusId(campusId)
                        .campusName(campusName)
                        .isEnable(1)
                        .build();
                list.add(teachingClassInput);
            }
        }
        return teachingClassService.addBatchTeachingClass(list, true);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer generationStudent(Long courseId, Integer studyNature, Integer flag, List<Long> teachingClassIds, String campusId) {
        //删除原来数据吗
        if (CollUtil.isNotEmpty(teachingClassIds)) {
            teachingClassStudentMapper.delete(new LambdaUpdateWrapper<TeachingClassStudent>().in(TeachingClassStudent::getTeachingClassId, teachingClassIds));
        }
        //获取学生列表
        List<Student> studentList = new ArrayList<>();
        List<Student> studentByCourse = this.getStudentNumByCourseAndNature(flag, courseId, studyNature, campusId);
        studentList.addAll(studentByCourse);
        studentList = studentList.stream().distinct().sorted(Comparator.comparing(Student::getClassId)).collect(Collectors.toList());
        if (CollUtil.isEmpty(studentList)) {
            return 0;
        }
        //按设计的班级数量平均分配(优先行政班放一起)
        int teachingClassNum = teachingClassIds.size();
        List<List<Student>> avgStudentList = ListUtil.splitAvg(studentList, teachingClassNum);
        //教学班对应学生
        List<TeachingClassStudent> teachingClassStudentList = new ArrayList<>();
        for (int i = 0; i < teachingClassNum; i++) {
            Long teachingClassId = teachingClassIds.get(i);
            avgStudentList.get(i).forEach(student -> {
                teachingClassStudentList.add(new TeachingClassStudent().setTeachingClassId(teachingClassId)
                        .setStudentId(student.getStudentId()).setFlag(1));
            });
        }
        //批量插入教学班学生，大数据批量插入
        Consumer<List<TeachingClassStudent>> studentConsumer = o -> teachingClassStudentMapper.addBatch(o);
        BatchInsert.batchSplitInsert(teachingClassStudentList, studentConsumer);
        return teachingClassStudentList.size();
    }

    @Override
    public Integer generationClassNum(List<Long> teachingClassIds, Integer studentNum) {
        //计算平均值
        int average = studentNum / teachingClassIds.size();
        //计算余数
        int remainder = studentNum % teachingClassIds.size();
        //计算每个教学班的人数
        List<Integer> classNumList = new ArrayList<>();
        for (int i = 0; i < teachingClassIds.size(); i++) {
            if (i < remainder) {
                classNumList.add(average + 1);
            } else {
                classNumList.add(average);
            }
        }
        //更新教学班容量
        for (int i = 0; i < teachingClassIds.size(); i++) {
            TeachingClass teachingClass = new TeachingClass();
            teachingClass.setTeachingClassId(teachingClassIds.get(i));
            teachingClass.setClassNum(classNumList.get(i));
            teachingClassService.updateById(teachingClass);
        }
        return teachingClassIds.size();
    }


    @Override
    public CourseSetting setAssessment(CourseSetting courseSetting) {
        courseSettingService.saveOrUpdate(courseSetting);
        return courseSetting;
    }


    @Override
    public SegmentSetting setSegmentAssessment(SegmentSetting segmentSetting) {
        segmentSettingService.saveOrUpdate(segmentSetting);
        return segmentSetting;
    }

    @Override
    public String courseToExcel(Integer taskId, String collegeId, String collegeName, String courseTypeId, String courseType, Integer courseNature, Integer studyNature) {
        TaskInfo taskInfo = taskInfoService.getById(taskId);
        String storePath = "/" + taskInfo.getTaskName() + "/课程开课计划-" + LocalDate.now() + ".xlsx";
        String targetPath = GlobalConstant.OUTPUT_EXCEL_PATH + storePath;
        FileUtil.del(targetPath);
        ExcelWriter writer = ExcelUtil.getWriter(targetPath);

        Page<CourseApplyDto> page = new Page<>(1, -1);
        IPage<CourseApplyDto> courseDtos = courseMapper.getCourseByConditions(taskId, page, null, courseTypeId, courseNature, null, collegeId, null);
        List<CourseApplyDto> resultList = courseDtos.getRecords();

        writer.merge(11, taskInfo.getSchoolName() + "开课计划 \n " + taskInfo.getTermName(), false);
        writer.setRowHeight(0, 40);
        StringBuilder stringBuilder = StrUtil.builder().append("开课单位：").append(StrUtil.isBlank(collegeId) ? "全部学院  " : collegeName + "  ")
                .append("课程类别：").append(StrUtil.isBlank(courseTypeId) ? "全部  " : courseType + "  ")
                .append("课程性质：").append(ObjUtil.isNull(courseNature) ? "全部  " : (courseNature == 1 ? "必修  " : "选修  "))
                .append("共：").append(resultList.size()).append("门  ")
                .append("导出时间：").append(DateUtil.now());
        writer.merge(11, stringBuilder, false);
        List<List<String>> rows = new ArrayList<>();
        rows.add(Arrays.asList("代码", "名称", "开课单位", "课程类别", "课程性质", "学分", "总学时", "讲授学时", "实验学时", "上机学时", "其他学时", "审核意见"));
        List<String> row;
        for (CourseApplyDto item : resultList) {
            row = Arrays.asList(item.getCourseCode(), item.getCourseName(), item.getCollegeName(), item.getCourseType(),
                    item.getCourseNature().equals(1) ? "必修" : "选修", item.getCredit().toString(), item.getTotalHour().toString(), item.getLectureHour().toString(),
                    item.getExperimentalHour().toString(), item.getComputerHour().toString(), item.getOtherHour().toString(), item.getReviewComment());
            rows.add(row);
        }
        writer.write(rows, false);
        ExcelFormatUtil.setFont(writer);
        writer.close();
        return GlobalConstant.ACCESS_PATH + "/static/OutputExcel" + storePath;
    }

    @Override
    public String segmentToExcel(Integer taskId, String collegeId, String collegeName, String segmentTypeId, String segmentType) {
        TaskInfo taskInfo = taskInfoService.getById(taskId);
        String storePath = "/" + taskInfo.getTaskName() + "/环节开课计划-" + LocalDate.now() + ".xlsx";
        String targetPath = GlobalConstant.OUTPUT_EXCEL_PATH + storePath;
        FileUtil.del(targetPath);
        ExcelWriter writer = ExcelUtil.getWriter(targetPath);

        Page<SegmentDto> page = new Page<>(1, -1);
        IPage<SegmentDto> segmentDtos = segmentMapper.getSegmentByConditions(taskId, page, null, segmentTypeId, collegeId, null);
        List<SegmentDto> resultList = segmentDtos.getRecords();

        String title = "{}开课计划  \n  {}";
        writer.merge(6, StrUtil.format(title, taskInfo.getSchoolName(), taskInfo.getTermName()), false);
        writer.setRowHeight(0, 40);
        String filter = "开课单位:{}  环节类型:{}  共:{}门   导出时间:{} ";
        writer.merge(6, StrUtil.format(filter, StrUtil.isBlank(collegeId) ? "全部学院  " : collegeName,
                StrUtil.isBlank(segmentTypeId) ? "全部  " : segmentType, resultList.size(), DateUtil.now()), false);
        List<List<String>> rows = new ArrayList<>();
        rows.add(Arrays.asList("代码", "名称", "开课单位", "环节类型", "学分", "周数", "审核意见"));
        List<String> row;
        for (SegmentDto item : resultList) {
            row = Arrays.asList(item.getSegmentCode(), item.getSegmentName(), item.getCollegeName(), item.getSegmentType(), item.getCredit().toString(), item.getWeekNum().toString(), item.getReviewComment());
            rows.add(row);
        }
        writer.write(rows, false);
        ExcelFormatUtil.setFont(writer);
        writer.close();
        return GlobalConstant.ACCESS_PATH + "/static/OutputExcel" + storePath;
    }

    @Override
    public String courseApplyToExcel(Integer taskId, String collegeId, String collegeName, String courseTypeId, String courseType, Integer courseNature, Integer studyNature, List<Long> courseIds) {
        String now = DateUtil.now();
        TaskInfo taskInfo = taskInfoService.getById(taskId);
        String storePath = "/" + taskInfo.getTaskName() + "/课程申请开课变更-" + LocalDate.now() + ".xlsx";
        String targetPath = GlobalConstant.OUTPUT_EXCEL_PATH + storePath;
        FileUtil.del(targetPath);
        ExcelWriter writer = ExcelUtil.getWriter(targetPath);

        Page<CourseApplyDto> page = new Page<>(1, -1);
        IPage<CourseApplyDto> courseDtos = courseMapper.getCourseByConditions(taskId, page, 2, courseTypeId, courseNature, null, collegeId, null);
        List<CourseApplyDto> resultList = courseDtos.getRecords();
        if (CollUtil.isNotEmpty(courseIds)) {
            resultList = resultList.stream().filter(item -> courseIds.contains(item.getCourseId())).collect(Collectors.toList());
        }

        writer.merge(10, taskInfo.getSchoolName() + "申请开课变更 \n " + taskInfo.getTermName(), false);
        writer.setRowHeight(0, 40);
        StringBuilder stringBuilder = StrUtil.builder().append("开课单位：").append(StrUtil.isBlank(collegeId) ? "全部学院  " : collegeName + "  ")
                .append("课程类别：").append(StrUtil.isBlank(courseTypeId) ? "全部  " : courseType + "  ")
                .append("课程性质：").append(ObjUtil.isNull(courseNature) ? "全部  " : (courseNature == 1 ? "必修  " : "选修  "))
                .append("导出时间：").append(now);
        writer.merge(10, stringBuilder, false);
        List<List<String>> rows = new ArrayList<>();
        rows.add(Arrays.asList("代码", "名称", "开课单位", "课程类别", "课程性质", "学分", "总学时", "讲授学时", "实验学时", "上机学时", "其他学时"));
        List<String> row;
        for (CourseApplyDto item : resultList) {
            row = Arrays.asList(item.getCourseCode(), item.getCourseName(), item.getCollegeName(), item.getCourseType(),
                    item.getCourseNature().equals(1) ? "必修" : "选修", item.getCredit().toString(), item.getTotalHour().toString(), item.getLectureHour().toString(),
                    item.getExperimentalHour().toString(), item.getComputerHour().toString(), item.getOtherHour().toString());
            rows.add(row);
        }
        writer.write(rows, false);
        ExcelFormatUtil.setFont(writer);
        writer.close();
        return GlobalConstant.ACCESS_PATH + "/static/OutputExcel" + storePath;
    }

    @Override
    public String segmentApplyToExcel(Integer taskId, String collegeId, String collegeName, String segmentTypeId, String segmentType, List<Long> segmentIds) {
        String now = DateUtil.now();
        TaskInfo taskInfo = taskInfoService.getById(taskId);
        String storePath = "/" + taskInfo.getTaskName() + "/环节申请开课变更-" + LocalDate.now() + ".xlsx";
        String targetPath = GlobalConstant.OUTPUT_EXCEL_PATH + storePath;
        FileUtil.del(targetPath);
        ExcelWriter writer = ExcelUtil.getWriter(targetPath);

        Page<SegmentDto> page = new Page<>(1, -1);
        IPage<SegmentDto> segmentDtos = segmentMapper.getSegmentByConditions(taskId, page, 2, segmentTypeId, collegeId, null);
        List<SegmentDto> resultList = segmentDtos.getRecords();
        if (CollUtil.isNotEmpty(segmentIds)) {
            resultList = resultList.stream().filter(item -> segmentIds.contains(item.getSegmentId())).collect(Collectors.toList());
        }

        String title = "{}环节申请开课变更  \n  {}";
        writer.merge(5, StrUtil.format(title, taskInfo.getSchoolName() + collegeName, taskInfo.getTermName()), false);
        writer.setRowHeight(0, 40);
        String filter = "开课单位:{}  环节类型:{}   导出时间:{} ";
        writer.merge(5, StrUtil.format(filter, StrUtil.isBlank(collegeId) ? "全部学院  " : collegeName,
                StrUtil.isBlank(segmentTypeId) ? "全部  " : segmentType, now), false);
        List<List<String>> rows = new ArrayList<>();
        rows.add(Arrays.asList("代码", "名称", "开课单位", "环节类型", "学分", "周数"));
        List<String> row;
        for (SegmentDto item : resultList) {
            row = Arrays.asList(item.getSegmentCode(), item.getSegmentName(), item.getCollegeName(), item.getSegmentType(), item.getCredit().toString(), item.getWeekNum().toString());
            rows.add(row);
        }
        writer.write(rows, false);
        ExcelFormatUtil.setFont(writer);
        writer.close();
        return GlobalConstant.ACCESS_PATH + "/static/OutputExcel" + storePath;
    }


}
